package cn.swenty.backstage.sso.client.shiro.realm;

import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthResourceResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.swenty.backstage.framework.common.Global;
import cn.swenty.backstage.framework.common.model.User;

/** 
* @ClassName: OAuth2Realm 
* @Description: TODO(Open Authoriztaion2.0实现) 
* @author sunyang
* @date 2018年1月26日 下午1:51:48 
*  
*/
public abstract class OAuth2Realm extends AuthorizingRealm {
	
	private static Logger logger = LoggerFactory.getLogger(OAuth2Realm.class);

	private String accessTokenUrl;
	private String userInfoUrl;
	private String redirectUrl;
	private String logoutUrl;

	public String getLogoutUrl() {
		return logoutUrl;
	}

	public void setLogoutUrl(String logoutUrl) {
		this.logoutUrl = logoutUrl;
	}

	public String getAccessTokenUrl() {
		return accessTokenUrl;
	}

	public String getUserInfoUrl() {
		return userInfoUrl;
	}

	public String getRedirectUrl() {
		return redirectUrl;
	}

	public void setAccessTokenUrl(String accessTokenUrl) {
		this.accessTokenUrl = accessTokenUrl;
	}

	public void setUserInfoUrl(String userInfoUrl) {
		this.userInfoUrl = userInfoUrl;
	}

	public void setRedirectUrl(String redirectUrl) {
		this.redirectUrl = redirectUrl;
	}

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		User user = Global.getCurrentUser();
		logger.debug("get user permissions");
		String functions[] = permissions(user);
		if(functions != null){
			for (int i = 0; i < functions.length; i++) {
				authorizationInfo.addStringPermission(functions[i]);
			}
		}
		return  new SimpleAuthorizationInfo();
	}
	
	
	@Override
	public boolean supports(AuthenticationToken token) {
		logger.debug("judge token class = '"+token.toString()+"'");
		return token instanceof OAuth2Token;// 表示此Realm只支持OAuth2Token类型
	}

	
	public abstract User getUser(String username);
	
	public abstract String[] permissions(User user);
	
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		OAuth2Token oAuth2Token = (OAuth2Token) token;
		String code = oAuth2Token.getAuthCode();
		logger.debug("send code '"+code+"' to sso server then get username");
		// 取到用户登录的用户名
		String username = extractUsername(code);
		logger.info("from sso server  get username '"+code+"'");
		
		logger.info("send '"+username+"' then get user");
		User user = getUser(username);
		if(user!=null){
			Global.setCurrentUser(user);
			return new SimpleAuthenticationInfo(username, code, getName());
		}else{
			throw new UnknownAccountException();// 没找到帐号
		}
	}

	private String extractUsername(String code) {

		try {
			OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());

			OAuthClientRequest accessTokenRequest = OAuthClientRequest.tokenLocation(accessTokenUrl)
																	  .setGrantType(GrantType.AUTHORIZATION_CODE)
																	  .setCode(code)
																	  .setClientId("1")
																	  .setClientSecret("1")
																	  .setRedirectURI(redirectUrl)
																	  .buildQueryMessage();
			logger.debug("send auth code then get token");

			OAuthAccessTokenResponse oAuthResponse = oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST);

			String accessToken = oAuthResponse.getAccessToken();
			logger.debug("from "+userInfoUrl+" get access token "+accessToken);

			OAuthClientRequest userInfoRequest = new OAuthBearerClientRequest(userInfoUrl)
																		.setAccessToken(accessToken)
																		.buildQueryMessage();

			OAuthResourceResponse resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
			return resourceResponse.getBody(); 
		} catch (Exception e) {
			e.printStackTrace();
			throw new AuthenticationException(e);
		}
	}
}
